use std::{cmp, mem, sync::{atomic::{AtomicUsize, Ordering}, Arc, Weak}, time::{Duration, Instant}};

use parking_lot::MutexGuard;

use crate::{conn_pool::{Conn, IdleConn, PoolInternals, SharedPool}, event::{AcquireEvent, ReleaseEvent}, Extensions, ManageConnection};





pub(crate) static CONNECTION_ID: AtomicUsize = AtomicUsize::new(0);

pub(crate) fn drop_conns<M>(
    shared: &Arc<SharedPool<M>>,
    mut internals: MutexGuard<PoolInternals<M::Connection>>,
    conns: Vec<Conn<M::Connection>>,
) where
    M: ManageConnection,
{
    internals.num_conns -= conns.len() as u32;
    establish_idle_connections(shared, &mut internals);
    drop(internals); // make sure we run connection destructors without this locked

    for conn in conns {
        let event = ReleaseEvent {
            id: conn.id,
            age: conn.birth.elapsed(),
        };
        shared.config.event_handler.handle_release(event);
        shared.config.connection_customizer.on_release(conn.conn);
    }
}

pub(crate) fn establish_idle_connections<M>(
    shared: &Arc<SharedPool<M>>,
    internals: &mut PoolInternals<M::Connection>,
) where
    M: ManageConnection,
{
    let min = shared.config.min_idle.unwrap_or(shared.config.max_size);
    let idle = internals.conns.len() as u32;
    for _ in idle..min {
        add_connection(shared, internals);
    }
}

pub(crate) fn add_connection<M>(shared: &Arc<SharedPool<M>>, internals: &mut PoolInternals<M::Connection>)
where
    M: ManageConnection,
{
    if internals.num_conns + internals.pending_conns >= shared.config.max_size {
        return;
    }

    internals.pending_conns += 1;
    inner(Duration::from_secs(0), shared);

    fn inner<M>(delay: Duration, shared: &Arc<SharedPool<M>>)
    where
        M: ManageConnection,
    {
        let new_shared = Arc::downgrade(shared);
        shared.config.thread_pool.execute_after(delay, move || {
            let shared = match new_shared.upgrade() {
                Some(shared) => shared,
                None => return,
            };

            let conn = shared.manager.connect().and_then(|mut conn| {
                shared
                    .config
                    .connection_customizer
                    .on_acquire(&mut conn)
                    .map(|_| conn)
            });
            match conn {
                Ok(conn) => {
                    let id = CONNECTION_ID.fetch_add(1, Ordering::Relaxed) as u64;

                    let event = AcquireEvent { id };
                    shared.config.event_handler.handle_acquire(event);

                    let mut internals = shared.internals.lock();
                    internals.last_error = None;
                    let now = Instant::now();
                    let conn = IdleConn {
                        conn: Conn {
                            conn,
                            extensions: Extensions::new(),
                            birth: now,
                            id,
                        },
                        idle_start: now,
                    };
                    internals.conns.push(conn);
                    internals.pending_conns -= 1;
                    internals.num_conns += 1;
                    shared.cond.notify_one();
                }
                Err(err) => {
                    shared.internals.lock().last_error = Some(err.to_string());
                    shared.config.error_handler.handle_error(err);
                    let delay = cmp::max(Duration::from_millis(200), delay);
                    let delay = cmp::min(shared.config.connection_timeout / 2, delay * 2);
                    inner(delay, &shared);
                }
            }
        });
    }
}

pub(crate) fn reap_connections<M>(shared: &Weak<SharedPool<M>>)
where
    M: ManageConnection,
{
    let shared = match shared.upgrade() {
        Some(shared) => shared,
        None => return,
    };

    let mut old = Vec::with_capacity(shared.config.max_size as usize);
    let mut to_drop = vec![];

    let mut internals = shared.internals.lock();
    mem::swap(&mut old, &mut internals.conns);
    let now = Instant::now();
    for conn in old {
        let mut reap = false;
        if let Some(timeout) = shared.config.idle_timeout {
            reap |= now - conn.idle_start >= timeout;
        }
        if let Some(lifetime) = shared.config.max_lifetime {
            reap |= now - conn.conn.birth >= lifetime;
        }
        if reap {
            to_drop.push(conn.conn);
        } else {
            internals.conns.push(conn);
        }
    }
    drop_conns(&shared, internals, to_drop);
}
